home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 20 code / Scripting the Finder / Finder Tricks / EventHandler.cp < prev    next >
Encoding:
Text File  |  1994-10-04  |  11.6 KB  |  470 lines  |  [TEXT/MMCC]

  1. /*================================================================================
  2.     EventHandler.c
  3.     
  4.     ©1991-4 Greg Anderson
  5.     greggor@apple.com
  6.         
  7.     This code handles the main macintosh event loop    
  8. ================================================================================*/
  9. #include <EPPC.h>
  10. #include <AppleEvents.h>
  11. #include <Errors.h>
  12.  
  13. #include "EventHandler.h"
  14. #include "NextEvent.h"
  15. #include "MenuHandler.h"
  16. #include "WindowHandler.h"
  17.  
  18. #include "Main.h"
  19.  
  20. #include "MacUtilities.h"
  21. #include "ReportError.h"
  22.  
  23. #ifndef Exceptions_h
  24. #include "Exceptions.h"
  25. #endif
  26.  
  27. //
  28. // Prototypes for private functions:
  29. //
  30. void Idle(EventRecord* nullEvent);
  31.  
  32. void                ProcessModelessDialog( EventRecord* theEvent  );
  33. Boolean                ProcessMouseEvent( EventRecord* theEvent  );
  34. void                SetupCursorShape( Point theMouse, RgnHandle mouseRgn );
  35.  
  36. //----------------------------------------------------------------------------------------
  37. // Idle: 
  38. //----------------------------------------------------------------------------------------
  39. void Idle(EventRecord* nullEvent)
  40. {
  41.  
  42. } // Idle 
  43.  
  44. //----------------------------------------------------------------------------------------
  45. // HandleEvents: 
  46. //
  47. // Main event processor
  48. //----------------------------------------------------------------------------------------
  49. void HandleEvents( RgnHandle mouseRegion )
  50. {
  51.     EventRecord        theEvent;
  52.     Boolean            isDialogEvent;
  53.     long            theSleep;
  54.     Point            mouse;
  55.     char            keyPressed;
  56.     OSErr            err = noErr;
  57.     
  58.     //
  59.     // This routine watches the cursor as it moves across the
  60.     // window & changes its shape as necessary.
  61.     //
  62.     GetMouse( &mouse );
  63.     LocalToGlobal( &mouse );
  64.     // SetupCursorShape( mouse, mouseRegion);
  65.     
  66.     //
  67.     // Get the next event, whatever type it might be.
  68.     //
  69.     // 'NextEvent' calls either GetNextEvent or WaitNextEvent
  70.     // (preferably the later).  It does some processing
  71.     // to make deactivate, suspend, resume and mouse-moved
  72.     // events easier to interpret.
  73.     //
  74.     // If 'GetNextEvent' is called, mouse-moved events are
  75.     // simulated, so the same cursor-tracking code may be
  76.     // used in multifinder and non-multifinder environments.
  77.     //
  78.     theSleep = 10;
  79.     isDialogEvent = NextEvent( everyEvent, &theEvent, theSleep, mouseRegion );
  80.  
  81. #ifdef ShowMouseRgn
  82.     SetPort(gWindowMgrPort);
  83.     SetClip(gUniverseRgn);
  84.     InvertRgn(mouseRegion);
  85.     InvertRgn(mouseRegion);
  86. #endif
  87.  
  88.     //
  89.     // It seems that the only way to do accurate mouse tracking
  90.     // is to call SetupCursorShape before and after NextEvent.
  91.     //
  92.     // Passing nil as the mouseRgn prevents all of the complex region
  93.     // calculations from being done twice.
  94.     //
  95.     // SetupCursorShape( theEvent.where, nil );
  96.     
  97.     //
  98.     // Set up a failure handler for commands that just cannot be completed
  99.     //
  100.     Try
  101.     {
  102.         Boolean eventHandled = false;
  103.         
  104.         //
  105.         // Here is the dreaded Event Switch Statement.
  106.         //
  107.         // I should write a better event processor -- or better yet,
  108.         // switch to MacApp.  :>
  109.         //
  110.         // A word of warning for the unwary:  NextEvent() mauls
  111.         // theEvent.what, so some of the items in this switch statement
  112.         // might be undefined if GetNextEvent or WaitNextEvent is
  113.         // called directly.  Activate, deactivate, suspend and resume
  114.         // events are all mauled by NextEvent.  See NextEvent.c for
  115.         // code that detects these events (or better yet, include
  116.         // NextEvent.c in your project and use this code as a sample,
  117.         // and you'll be MultiFinder aware).
  118.         //
  119.         switch( theEvent.what )
  120.         {
  121.             //
  122.             // First check for high-level events (e.g. AppleEvents)
  123.             //
  124.             case kHighLevelEvent:
  125.             {
  126.                 AEProcessAppleEvent( &theEvent );
  127.                 eventHandled = true;
  128.                 break;
  129.             }
  130.             
  131.             //
  132.             // If the event is a mousedown event, theEvent.message is undefined,
  133.             // but theEvent.where specifies where the mouse was located when the
  134.             // button went down.
  135.             //
  136.             case mouseDown:    
  137.             {
  138.                 eventHandled = ProcessMouseEvent( &theEvent );
  139.                 break;
  140.             }
  141.             
  142.             //
  143.             // If the event is a keyDown or autoKey event, theEvent.message
  144.             // contains the character code & key code of the key pressed
  145.             // in its low word.
  146.             //
  147.             case autoKey:
  148.             {
  149.                 //
  150.                 // Don't allow autokeys to work with menu equivalents
  151.                 //
  152.                 if( (theEvent.modifiers & cmdKey) != 0 )
  153.                 {
  154.                     eventHandled = true;
  155.                     break;
  156.                 }
  157.             }
  158.             
  159.             case keyDown:
  160.             {
  161.                 keyPressed = (char)(theEvent.message & charCodeMask);
  162.                 
  163.                 //
  164.                 // Handle command-key equivalents of menu functions
  165.                 //
  166.                 if( (theEvent.modifiers & cmdKey) != 0)
  167.                 {
  168.                     SetupMenuItems();
  169.                     ProcessMenuSelection( MenuKey(keyPressed) );
  170.                     eventHandled = true;
  171.                 }
  172.                 else
  173.                 {
  174.                     eventHandled = FrontWindowHandler()->KeyDown(&theEvent, keyPressed);
  175.                 }
  176.                 
  177.                 break;
  178.             }
  179.             
  180.             //
  181.             // If the event is an update event or an activate event,
  182.             // theEvent.message contains a pointer to the window 
  183.             // receiving the event
  184.             //
  185.             case updateEvt:
  186.             {
  187.                 GetWindowHandler((WindowPtr)(theEvent.message))->Update(&theEvent);
  188.                 break;
  189.             }
  190.             
  191.             //
  192.             // An activate event can mean that a window is being activated,
  193.             // OR it could mean that a window is being deactivated.
  194.             //
  195.             // However, NextEvent decodes the event record and returns
  196.             // 'deactivateEvt' if the event was a deactivate; this
  197.             // simplifies this switch statement.  Be careful when copying
  198.             // this code--if you call WaitNextEvent directly, the
  199.             // translation will not be done and this code won't work.
  200.             //
  201.             case activateEvt:
  202.             {
  203.                 GetWindowHandler((WindowPtr)(theEvent.message))->Activate(&theEvent);
  204.                 break;
  205.             }
  206.             
  207.             case deactivateEvt:
  208.             {
  209.                 GetWindowHandler((WindowPtr)(theEvent.message))->Deactivate(&theEvent);
  210.                 break;
  211.             }
  212.             
  213.             //
  214.             // Suspend and resume events are treated like activate and
  215.             // deactivate messages.  (Note that FrontWindow() might
  216.             // return nil; ActivateWindow and DeactivateWindow should
  217.             // catch this and exit gracefully.)
  218.             //
  219.             // Note that NextEvent decodes app4Evt's and returns either
  220.             // resumeEvt, suspendEvt or mouseMovedEvt.
  221.             //
  222.             case resumeEvt:
  223.             {
  224.                 GetWindowHandler((WindowPtr)(theEvent.message))->Resume(&theEvent);
  225.                 break;
  226.             }
  227.             
  228.             case suspendEvt:
  229.             {
  230.                 GetWindowHandler((WindowPtr)(theEvent.message))->Suspend(&theEvent);
  231.                 break;
  232.             }
  233.             
  234.             //
  235.             // The mouseRegion is recalculated on every event,
  236.             // so nothing special needs to be done on mouseMovedEvt.
  237.             //
  238.             case mouseMovedEvt:
  239.             {
  240.                 break;
  241.             }
  242.         }
  243.  
  244.         //
  245.         // Check for modeless dialog events & pass them to the
  246.         // appropriate modeless dialog box.
  247.         //
  248.         // Do not pass events already handled above, though.
  249.         //
  250.         if( isDialogEvent && (eventHandled == false))
  251.         {
  252.             ProcessModelessDialog( &theEvent );
  253.         }
  254.                 
  255.         //
  256.         // Do idle-type-stuff
  257.         //
  258.         Idle(&theEvent);
  259.     }
  260.     Catch(err)
  261.     {
  262.         if((err != eNoWindowHandler) && (err != userCanceledErr))
  263.         {
  264.             //
  265.             // It would be nice if we had better error reporting
  266.             //
  267.             ReportError( "\pThe command could not be completed", err );
  268.         }
  269.     }
  270. } // HandleEvents 
  271.  
  272. //----------------------------------------------------------------------------------------
  273. // ProcessModelessDialog: 
  274. //
  275. // Modeless-dialog event.
  276. //----------------------------------------------------------------------------------------
  277. void ProcessModelessDialog( EventRecord* theEvent  )
  278. {
  279.     DialogPtr            theDialog;
  280.     short                itemHit;
  281.  
  282.     //
  283.     // If the event is a modeless dialog event, pass it to
  284.     // DialogSelect.
  285.     //
  286.     // If DialogSelect returns 'true', that indicates that
  287.     // the user has interacted with the dialog in some way.
  288.     // If this is the case, theDialog will point to the
  289.     // dialog box in question, and itemHit will contain the
  290.     // number of the item clicked on.
  291.     //
  292.     // We determine which dialog was clicked on by examining
  293.     // a field in the record pointed to by the dialog's
  294.     // refCon.
  295.     //
  296.     if( DialogSelect( theEvent, &theDialog, &itemHit ) )
  297.     {
  298.         //
  299.         // Pass the event record and itemHit to the window handler
  300.         //
  301.         GetWindowHandler(theDialog)->DialogManagerEvent(theEvent, itemHit);
  302.     }
  303. } // ProcessModelessDialog 
  304.  
  305. //----------------------------------------------------------------------------------------
  306. // ProcessMouseEvent: 
  307. //
  308. // Mouse-click event.
  309. //
  310. // Strangely enough, theEvent->message doesn't tell us which window
  311. // was clicked on.  The Macintosh toolbox function 'FindWindow' can
  312. // determine this for us, however.
  313. //----------------------------------------------------------------------------------------
  314. Boolean ProcessMouseEvent( EventRecord* theEvent  )
  315. {
  316.     WindowPtr    whichWindow;
  317.     Boolean        eventHandled = false;
  318.     
  319.     switch( FindWindow( theEvent->where, &whichWindow ) )
  320.     {
  321.         case inMenuBar:
  322.         {
  323.             SetupMenuItems();
  324.             ProcessMenuSelection( MenuSelect(theEvent->where) );
  325.             eventHandled = true;
  326.             break;
  327.         }
  328.         
  329.         case inSysWindow:
  330.         {
  331.             SystemClick( theEvent,(WindowPtr)whichWindow );
  332.             eventHandled = true;
  333.             break;
  334.         }
  335.         
  336.         case inContent:
  337.         {
  338.             //
  339.             // If the window clicked on is not frontmost, make it frontmost.
  340.             //
  341.             if( whichWindow != FrontWindow() )
  342.             {
  343.                 SelectWindow(whichWindow);
  344.                 eventHandled = true;
  345.             }
  346.             else
  347.             {
  348.                 eventHandled = GetWindowHandler(whichWindow)->ContentClick(theEvent);
  349.             }
  350.             break;
  351.         }
  352.         
  353.         case inDrag:
  354.         {
  355.             DragWindow( (WindowPtr)whichWindow,theEvent->where,&gUniverseRect);
  356.             eventHandled = true;
  357.             break;
  358.         }
  359.             
  360.         case inGrow:
  361.         {
  362.             GetWindowHandler(whichWindow)->ResizeWindow(theEvent->where);
  363.             eventHandled = true;
  364.             break;
  365.         }
  366.         
  367.         case inGoAway:
  368.         {
  369.             if( TrackGoAway(whichWindow,theEvent->where) )
  370.             {
  371.                 //
  372.                 // We only have one window, so close it
  373.                 //
  374.                 GetWindowHandler(whichWindow)->CloseWindowByUser();
  375.             }
  376.             eventHandled = true;
  377.             break;
  378.         }
  379.     }
  380.     return eventHandled;
  381. } // ProcessMouseEvent 
  382.  
  383. //----------------------------------------------------------------------------------------
  384. // SetupCursorShape: 
  385. //
  386. // Change the shape of the cursor based on its location
  387. //----------------------------------------------------------------------------------------
  388. void SetupCursorShape( Point theMouse, RgnHandle mouseRgn )
  389. {
  390.     OSErr err = noErr;
  391.             
  392.     //
  393.     // Try to set the shape of the cursor
  394.     //
  395.     Try
  396.     {
  397.         WindowPtr inWindow = nil;
  398.         Rect tRect;
  399.         Rect windowRect;
  400.         
  401.         //
  402.         // Is there a window?  If not, fail (error code is ignored, so it
  403.         // doesn't matter what it is)
  404.         //
  405.         inWindow = FrontWindow();
  406.         if(inWindow == nil)
  407.             Throw(-1);
  408.         
  409.         SetPort(inWindow);
  410.         GetGlobalWindowLocation(inWindow, &windowRect);    // inWindow->port.portRect
  411.                 
  412.         //
  413.         // If the point is not inside the front window,
  414.         // then set the cursor to an arrow, and set the mouse
  415.         // region to the universe minus the space taken by
  416.         // the front window
  417.         //
  418.         if(PtInRect(theMouse, &windowRect) == false)
  419.         {
  420.             ChangeCursor(0);
  421.             if(mouseRgn != nil)
  422.             {
  423.                 RectRgn(mouseRgn, &windowRect);
  424.                 XorRgn(mouseRgn, gUniverseRgn, mouseRgn);
  425.             }
  426.         }
  427.         else
  428.         {
  429.             //
  430.             // Do all of our work in local coordinates
  431.             //
  432.             GlobalToLocal(&theMouse);    
  433.  
  434.             //
  435.             // By default, the mouse region is a one-pixel region
  436.             // that just surrounds the cursor.  TWindowHandler::SetupCursorShape
  437.             // may change this region to something else
  438.             //
  439.             SetRect(&tRect, theMouse.h, theMouse.v, theMouse.h + 1, theMouse.v + 1);
  440.             if(mouseRgn != nil)
  441.                 RectRgn(mouseRgn, &tRect);
  442.  
  443.             //
  444.             // If this window doesn't have a window handler, then
  445.             // GetWindowHandler will fail.  If SetupCursorShape
  446.             // returns false, then the handler did not have a specific
  447.             // shape to set the cursor to; set it to an arrow.
  448.             //            
  449.             if(GetWindowHandler(inWindow)->SetupCursorShape(theMouse, mouseRgn) == false)
  450.                 ChangeCursor(0);
  451.             
  452.             //
  453.             // Translate the region back to global coordinates
  454.             //
  455.             if(mouseRgn != nil)
  456.                 RgnToGlobal(mouseRgn);
  457.         }
  458.     }
  459.     Catch(err)
  460.     {
  461.         //
  462.         // If we couldn't handle the SetupCursorShape event, then
  463.         // the cursor is an arrow everywhere
  464.         //
  465.         ChangeCursor(0);
  466.         if(mouseRgn != nil)
  467.             RectRgn(mouseRgn, &gUniverseRect);
  468.     }
  469. } // SetupCursorShape 
  470.